package org.mbds.android.emsc.seismicportal;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

/**
 * SeismicPortalREST
 * @author Anne-Marie Lesas
 * Seismic portal Web services access
 */

public class SeismicPortalREST {
	
	public static final String TAG = "SeismicPortalREST";
	public static final String ENCODING = "UTF-8";
	public static BasicHttpContext localContext = new BasicHttpContext();
	
	/*
	 * URL of seismic portal WS (currently the test environment)
	 */
	public static String HOST_URL = "http://www.seismicportal.eu";
	/*
	 * Port of seismic portal WS (currently the test environment)
	 */
	public static int HOST_PORT = 80;
	/*
	 * Counts
	 */
	public static String PATH_COUNTS = "/fdsnws/event/1/counts";
	/*
	 * Query
	 */
	public static String PATH_QUERY = "/fdsnws/event/1/query";
	/*
	 * Version
	 */
	public static String PATH_VERSION = "/fdsnws/event/1/version";
	/*
	 * Catalogs
	 */
	public static String PATH_CATALOGS = "/fdsnws/event/1/catalogs";
	/*
	 * Contributors
	 */
	public static String PATH_CONTRIBUTORS = "/fdsnws/event/1/contributors";
	
	/**
	 * HTTP Methods
	 * @return enumeration
	 */
	public static enum Method {
		POST, GET, PUT, DELETE
	}

	public static String APPLICATION_XML = "application/xml";
	public static String APPLICATION_JSON = "application/json";
	public static String TEXT_HTML = "text/html";
	
	
	/**
	 * isReachable
	 * @return boolean
	 */
	public static Boolean isReachable() {
		Log.d(TAG, "Trying to reach: " + HOST_URL);

		Socket bound = null;

		try {
			InetAddress address = InetAddress.getByName(HOST_URL);
			bound = new Socket(address, HOST_PORT);
		} catch (UnknownHostException e) {
			Log.d(TAG, "UnknownHostException: " + e.getMessage());
		} catch (IOException e) {
			Log.d(TAG, "IOException: " + e.getMessage());
		}

		if (bound != null && bound.isConnected()) {
			try {
				bound.close();
			} catch (IOException e) {
				//Log.d(TAG, "IOException: " + e.getMessage());
			}
			return true;
		}

		return false;
	}	
	
	/**
	 * querySeismicEvents
	 * @param starttime
	 * @param endtime
	 * @param minlatitude
	 * @param maxlatitude
	 * @param minlongitude
	 * @param maxlongitude
	 * @param minmagnitude
	 * @param maxmagnitude
	 * @param magtype
	 * @param catalog
	 * @param contributor
	 * @param maxdepth
	 * @param mindepth
	 * @param latitude
	 * @param longitude
	 * @param maxradius
	 * @param minradius
	 * @param updatedafter
	 * @param orderby
	 * @param eventid
	 * @param includearrivals
	 * @param includeallmagnitudes
	 * @param includeallorigins
	 * @param limit
	 * @param offset
	 * @param format
	 * @param callback
	 * @return seismic events QuakeML String
	 */
	public static ArrayList<SeismicEvent> querySeismicEvents(String starttime, 
			String endtime, Double minlatitude, Double maxlatitude, 
			Double minlongitude, Double maxlongitude, Double minmagnitude,
			Double maxmagnitude, String magtype, String catalog, String contributor,
			Double maxdepth, Double mindepth, Double latitude, Double longitude,
			Double maxradius, Double minradius, String updatedafter, String orderby,
			Long eventid, Boolean includearrivals, Boolean includeallmagnitudes,
			Boolean includeallorigins, Integer limit, Integer offset, String format,
			String callback) {
		
		String filter="";
		if (starttime!=null) filter+=QuakeML.STARTTIME+"="+starttime;
		if (filter.length()>0) filter+="&";
		if (endtime!=null) filter+=QuakeML.ENDTIME+"="+endtime;
		if (filter.length()>0) filter+="&";
		if (minlatitude!=null) filter+=QuakeML.MINLATITUDE+"="+minlatitude;
		if (filter.length()>0) filter+="&";
		if (maxlatitude!=null) filter+=QuakeML.MAXLATITUDE+"="+maxlatitude;
		if (filter.length()>0) filter+="&";
		if (minlongitude!=null) filter+=QuakeML.MINLONGITUDE+"="+minlongitude;
		if (filter.length()>0) filter+="&";
		if (maxlongitude!=null) filter+=QuakeML.MAXLONGITUDE+"="+maxlongitude;
		if (filter.length()>0) filter+="&";
		if (minmagnitude!=null) filter+=QuakeML.MINMAGNITUDE+"="+minmagnitude;
		if (filter.length()>0) filter+="&";
		if (maxmagnitude!=null) filter+=QuakeML.MAXMAGNITUDE+"="+maxmagnitude;
		if (filter.length()>0) filter+="&";
		if (magtype!=null) filter+=QuakeML.MAGTYPE+"="+magtype;
		if (filter.length()>0) filter+="&";
		if (catalog!=null) filter+=QuakeML.CATALOG+"="+catalog;
		if (filter.length()>0) filter+="&";
		if (contributor!=null) filter+=QuakeML.CONTRIBUTOR+"="+contributor;
		if (filter.length()>0) filter+="&";
		if (maxdepth!=null) filter+=QuakeML.MAXDEPTH+"="+maxdepth;
		if (filter.length()>0) filter+="&";
		if (mindepth!=null) filter+=QuakeML.MINDEPTH+"="+mindepth;
		if (filter.length()>0) filter+="&";
		if (latitude!=null) filter+=QuakeML.LATITUDE+"="+latitude;
		if (filter.length()>0) filter+="&";
		if (longitude!=null) filter+=QuakeML.LONGITUDE+"="+longitude;
		if (filter.length()>0) filter+="&";
		if (maxradius!=null) filter+=QuakeML.MAXRADIUS+"="+maxradius;
		if (filter.length()>0) filter+="&";
		if (minradius!=null) filter+=QuakeML.MINRADIUS+"="+minradius;
		if (filter.length()>0) filter+="&";
		if (updatedafter!=null) filter+=QuakeML.UPDATEAFTER+"="+updatedafter;
		if (filter.length()>0) filter+="&";
		if (orderby!=null) filter+=QuakeML.ORDERBY+"="+orderby;
		if (filter.length()>0) filter+="&";
		if (eventid!=null) filter+=QuakeML.EVENTID+"="+eventid;
		if (filter.length()>0) filter+="&";
		if (includearrivals!=null) filter+=QuakeML.INCLUDEARRIVALS+"="+includearrivals;
		if (filter.length()>0) filter+="&";
		if (includeallmagnitudes!=null) filter+=QuakeML.INCLUDEALLMAGNITUDES+"="+includeallmagnitudes;
		if (filter.length()>0) filter+="&";
		if (includeallorigins!=null) filter+=QuakeML.INCLUDEALLORIGINS+"="+includeallorigins;
		if (filter.length()>0) filter+="&";
		if (limit!=null) filter+=QuakeML.LIMIT+"="+limit;
		if (filter.length()>0) filter+="&";
		if (offset!=null) filter+=QuakeML.OFFSET+"="+offset;
		if (filter.length()>0) filter+="&";
		if (format!=null) filter+=QuakeML.FORMAT+"="+format;
		if (filter.length()>0) filter+="&";
		if (callback!=null) filter+=QuakeML.CALLBACK+"="+callback;
		
		if (filter.length()>0) filter="?"+filter;
		return QuakeML.getEvents(query(PATH_QUERY, filter, Method.GET, APPLICATION_XML, null));
	}
	
	/**
	 * Query
	 * @param path
	 * @param method (GET by default)
	 * @param accept (XML by default)
	 * @param object (xml, json, text...)
	 * @return String (XML by default)
	 */
	public static String query(String path, String filter, 
			Method method, String accept, String o) {
		
		if (method==null) method=Method.GET;
		if (accept==null) accept=APPLICATION_XML;
		
		URI uri;
		try {
			uri = new URI(HOST_URL
					+ ":"
					+ HOST_PORT
					+ path
					+ filter.replace(" ", "%20").replace((char) 42 + "", "%22")
					.replace("'", "%27"));
		} catch (URISyntaxException e) {
			Log.e(TAG, "Malformed URL", e);
			return null;
		}
		Log.d(TAG, "URL: " + uri.toString());

		DefaultHttpClient client = new DefaultHttpClient();

		HttpResponse response = null;
		StringEntity object = null;
		try {
			if (method == Method.POST) {
				HttpPost http = new HttpPost(uri);
				http.setHeader("Content-Type",
						"application/x-www-form-urlencoded");
				if (o != null) {
					object = new StringEntity(o, "UTF-8");
					object.setContentType(new BasicHeader(HTTP.CONTENT_TYPE,
							accept));
					http.setEntity(object);
				}
				http.addHeader("Content-Type", accept);
				http.addHeader("Accept", accept);
				response = client.execute(http, localContext);
			} else if (method == Method.PUT) {
				HttpPut http = new HttpPut(uri);
				if (o != null) {
					object = new StringEntity(o, "UTF-8");
					object.setContentType(accept);
					http.setEntity(object);
				}
				http.addHeader("Content-Type", accept);
				http.addHeader("Accept", accept);
				response = client.execute(http, localContext);
			} else if (method == Method.DELETE) {
				HttpDelete http = new HttpDelete(uri);
				http.setHeader("Content-Type",
						"application/x-www-form-urlencoded");
				http.addHeader("Accept", accept);
				http.addHeader("Accept-Charset", "utf-8");
				response = client.execute(http, localContext);
			} else {
				HttpGet http = new HttpGet(uri);
				http.addHeader("Accept", accept);
				http.addHeader("Accept-Charset", "utf-8");
				response = client.execute(http, localContext);
			}
		} catch (ClientProtocolException e) {
			Log.e(TAG, "ClientProtocol Error", e);
			return null;
		} catch (IOException e) {
			Log.e(TAG, "IO Error", e);
			return null;
		} catch (Exception e) {
			Log.e(TAG, "Error", e);
			return null;
		}

		if (response == null || response.getEntity() == null) {
			return null;
		}

		HttpEntity entity = response.getEntity();

		Log.d(TAG, "Response Entity: " + entity.getContentLength() + " bytes, "
				+ entity.getContentType() + "; " + entity.getContentEncoding()
				+ " " + EntityUtils.getContentCharSet(entity));

		String body = null;

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			entity.writeTo(out);
		} catch (IOException e) {
			Log.e(TAG, "IO Error", e);
			return null;
		}

		try {
			body = out.toString(ENCODING);
		} catch (UnsupportedEncodingException e) {
			Log.e(TAG, "Unsupported Encoding Error", e);
			return null;
		}

		Log.d(TAG, "Response: " + body);

		if (body == null || body.length() == 0) {
			return null;
		}

		return body;
	}
}
